﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Creventive.Wpf.ShellFactory.Workspace.Primitives
{
    /// <summary>
    ///   Provides various helper methods for workspace development.
    ///   WARNING! These method should only be used internally by workspaces. Only use them if you are creating your own workspaces!
    /// </summary>
    public static class WorkspaceHelper
    {
        /// <summary>
        ///   Attempts to close the given views as according to the provided flags. 
        ///   <br />WARNING! This method should only be used internally by workspaces. Only use it if you are creating your own workspace!
        /// </summary>
        /// <param name = "applicationViews">The application views. This argument may be null, and may contain nulls. The method treats nulls as being empty.</param>
        /// <param name = "flags">The flags.</param>
        /// <param name = "beforeCloseCallback">A callback which is called right before a view is actually closed. This is where the workspace should detach the given view from itself.</param>
        /// <param name = "closedCallback">A callback which is called when a view has been closed.</param>
        /// <param name = "customData">Custom data to route to the callback methods.</param>
        /// <returns>
        ///   <c>true</c> if all views were successfully closed (or there were no views to closed); <c>false</c> if one or more views refused to close.
        /// </returns>
        public static bool CloseViews(IEnumerable<ApplicationView> applicationViews, ApplicationViewClosingMode flags, Action<ApplicationView, object> beforeCloseCallback = null, Action<ApplicationView, object> closedCallback = null, object customData = null)
        {
            if (applicationViews == null)
                return true;

            ApplicationView[] closingViews = applicationViews.ToArray();
            switch (flags)
            {
                case ApplicationViewClosingMode.None:
                    // We only close a view if every single descendant view in the hierarchy can be closed.
                    if (CloseViews(closingViews, ApplicationViewClosingMode.QueryOnly, beforeCloseCallback, closedCallback, customData))
                        return CloseViews(closingViews, ApplicationViewClosingMode.NoQuery, beforeCloseCallback, closedCallback, customData);
                    else
                        return false;

                case ApplicationViewClosingMode.SequentialClose:
                    // Every view in the hierarchy is asked if it can close. If yes then it is closed immediately.
                    foreach (var view in closingViews)
                    {
                        if (view.QueryCanClose() && Workspace.CloseAllViews(view, ApplicationViewClosingMode.SequentialClose))
                        {
                            if (beforeCloseCallback != null)
                                beforeCloseCallback.Invoke(view, customData);
                            view.NotifyClose();
                            if (closedCallback != null)
                                closedCallback.Invoke(view, customData);
                        }
                        else
                            return false;
                    }
                    return true;

                case ApplicationViewClosingMode.QueryOnly:
                    // Check every view in the hierarchy if it can close. Returns true only if every view is closeable. No views are actually closed in this mode.
                    return closingViews.All(view => Workspace.CloseAllViews(view, ApplicationViewClosingMode.QueryOnly) && view.QueryCanClose());

                case ApplicationViewClosingMode.NoQuery:
                    // Forces every single view in the hierarchy to close, depth first, without checking if they are closeable.
                    foreach (var view in closingViews)
                    {
                        if (beforeCloseCallback != null)
                            beforeCloseCallback.Invoke(view, customData);
                        Workspace.CloseAllViews(view, ApplicationViewClosingMode.NoQuery);
                        view.NotifyClose();
                        if (closedCallback != null)
                            closedCallback.Invoke(view, customData);
                    }
                    return true;

                default:
                    throw new ArgumentOutOfRangeException("flags");
            }
        }

        /// <summary>
        ///   Notifies the view in the given workspace item that it has been activated.
        ///   <br />WARNING! This method should only be used internally by workspaces. Only use it if you are creating your own workspace!
        /// </summary>
        /// <param name = "workspaceItem">The workspace item.</param>
        public static void NotifyActivated(WorkspaceItem workspaceItem)
        {
            NotifyActivated(workspaceItem.ApplicationView);
        }

        /// <summary>
        ///   Notifies the given workspace view that it has been activated.
        ///   <br />WARNING! This method should only be used internally by workspaces. Only use it if you are creating your own workspace!
        /// </summary>
        /// <param name = "applicationView">The application view.</param>
        public static void NotifyActivated(ApplicationView applicationView)
        {
            applicationView.NotifyActivated();
        }

        /// <summary>
        ///   Notifies the view in the given workspace item that it has been deactivated.
        ///   <br />WARNING! This method should only be used internally by workspaces. Only use it if you are creating your own workspace!
        /// </summary>
        /// <param name = "workspaceItem">The workspace item.</param>
        public static void NotifyDeactivated(WorkspaceItem workspaceItem)
        {
            NotifyDeactivated(workspaceItem.ApplicationView);
        }

        /// <summary>
        ///   Notifies the given workspace view that it has been deactivated.
        ///   <br />WARNING! This method should only be used internally by workspaces. Only use it if you are creating your own workspace!
        /// </summary>
        /// <param name = "applicationView">The application view.</param>
        public static void NotifyDeactivated(ApplicationView applicationView)
        {
            applicationView.NotifyDeactivated();
        }
    }
}